use std::{
    collections::HashMap,
    sync::{Arc, Mutex},
};

use datasource_connector::connector::SimpleDataSourceConnector;
use model_graph_types::{
    container::workflow::{
        DataOperatorMethod, DataOperatorStatement, WorkflowBlockExecuteResult,
        WorkflowBlockExecuteResultBuilder, WorkflowBlockExecuteResultStatus,
    },
    database::DataSource,
    datasource::{
        DataSourceConnector, DeleteConditionBuilder, QueryConditionBuilder, UpdateConditionBuilder,
    },
    modeling::{system_meta_database, Value},
};

use crate::workflow::{blocks::statements::_utils::datasource_by_id, context::WorkflowContext};

pub async fn execute(
    statement: &DataOperatorStatement,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<WorkflowBlockExecuteResult> {
    let result = _execute(statement, context).await;

    match result {
        Ok(v) => Ok(WorkflowBlockExecuteResultBuilder::default()
            .status(WorkflowBlockExecuteResultStatus::Succeeded)
            .source_handle("source")
            .result(v)
            .build()?),
        Err(err) => Ok(WorkflowBlockExecuteResultBuilder::default()
            .status(WorkflowBlockExecuteResultStatus::Succeeded)
            .source_handle("fail-branch")
            .build()?),
    }
}

async fn _execute(
    statement: &DataOperatorStatement,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<HashMap<String, Value>> {
    let method = &statement.method;
    match method {
        DataOperatorMethod::Find => _find(statement, context).await,
        DataOperatorMethod::FindOne => _findone(statement, context).await,
        DataOperatorMethod::Insert => _insert(statement, context).await,
        DataOperatorMethod::Update => _update(statement, context).await,
        DataOperatorMethod::Delete => _delete(statement, context).await,
        _ => Err(anyhow::anyhow!("不支持的方法:{:?}", method)),
    }
}

async fn _findone(
    statement: &DataOperatorStatement,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<HashMap<String, Value>> {
    //
    tracing::debug!("_findone statement.....");
    let mut result: HashMap<String, Value> = HashMap::new();
    let (connector, meta_datasource, datasource) =
        _connector(&statement.datasource, context.clone()).await?;
    let _domain = &statement.domain;
    // TODO 坚持domain是否已经下载
    let class_identifier = &statement.class;
    //
    let conditions = if let Some(c) = &statement.conditions {
        c.clone()
    } else {
        QueryConditionBuilder::default().build()?
    };

    let extentions = &statement.extentions;
    let options = None;

    let value = connector
        .instance_query_one(
            &meta_datasource,
            &datasource,
            class_identifier,
            conditions,
            extentions.clone(),
            options,
        )
        .await?;

    result.insert(String::from("result"), value);

    Ok(result)
}

async fn _find(
    statement: &DataOperatorStatement,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<HashMap<String, Value>> {
    //
    tracing::debug!("find statement.....");
    let mut result: HashMap<String, Value> = HashMap::new();
    let (connector, meta_datasource, datasource) =
        _connector(&statement.datasource, context.clone()).await?;
    let _domain = &statement.domain;
    // TODO 坚持domain是否已经下载
    let class_identifier = &statement.class;
    //
    let conditions = if let Some(c) = &statement.conditions {
        c.clone()
    } else {
        QueryConditionBuilder::default().build()?
    };

    let extentions = &statement.extentions;
    let options = None;

    let value = connector
        .instance_query(
            &meta_datasource,
            &datasource,
            class_identifier,
            conditions,
            extentions.clone(),
            options,
        )
        .await?;

    result.insert(String::from("result"), Value::Array(value.data));
    result.insert(String::from("total"), Value::Number(value.total.into()));
    Ok(result)
}

async fn _update(
    statement: &DataOperatorStatement,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<HashMap<String, Value>> {
    //
    tracing::debug!("_update statement.....");
    let mut result: HashMap<String, Value> = HashMap::new();
    let (connector, meta_datasource, datasource) =
        _connector(&statement.datasource, context.clone()).await?;
    let _domain = &statement.domain;
    // TODO 坚持domain是否已经下载
    let class_identifier = &statement.class;
    //
    let conditions = if let Some(c) = &statement.conditions {
        c.clone()
    } else {
        QueryConditionBuilder::default().build()?
    };

    let value = if let Some(v) = &statement.value {
        let mut object = HashMap::new();
        for (key, value) in v {
            object.insert(key.clone(), value.try_into()?);
        }
        object
    } else {
        //
        return Err(anyhow::anyhow!(""));
    };
    let mut builder = UpdateConditionBuilder::default();
    if let Some(attributes) = conditions.attributes {
        builder.attributes(attributes);
    }

    if let Some(relations) = conditions.relations {
        builder.relations(relations);
    }

    let options = None;

    let value = connector
        .instance_update(
            &meta_datasource,
            &datasource,
            class_identifier,
            builder.build()?,
            value,
            options,
        )
        .await?;
    result.insert(String::from("result"), value);
    Ok(result)
}

async fn _insert(
    statement: &DataOperatorStatement,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<HashMap<String, Value>> {
    //
    tracing::debug!("_insert statement.....");
    let mut result: HashMap<String, Value> = HashMap::new();
    let (connector, meta_datasource, datasource) =
        _connector(&statement.datasource, context.clone()).await?;
    let _domain = &statement.domain;
    // TODO 坚持domain是否已经下载
    let class_identifier = &statement.class;
    let value = if let Some(v) = &statement.value {
        let mut object = HashMap::new();
        for (key, value) in v {
            object.insert(key.clone(), value.try_into()?);
        }
        object
    } else {
        //
        return Err(anyhow::anyhow!(""));
    };
    let options = None;

    let value = connector
        .instance_insert(
            &meta_datasource,
            &datasource,
            class_identifier,
            value,
            options,
        )
        .await?;
    result.insert(String::from("result"), value);
    Ok(result)
}

async fn _delete(
    statement: &DataOperatorStatement,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<HashMap<String, Value>> {
    //
    tracing::debug!("_delete statement.....");
    let mut result: HashMap<String, Value> = HashMap::new();
    let (connector, meta_datasource, datasource) =
        _connector(&statement.datasource, context.clone()).await?;
    let _domain = &statement.domain;
    // TODO 坚持domain是否已经下载
    let class_identifier = &statement.class;
    //
    let conditions = if let Some(c) = &statement.conditions {
        c.clone()
    } else {
        QueryConditionBuilder::default().build()?
    };
    let mut builder = DeleteConditionBuilder::default();
    if let Some(attributes) = conditions.attributes {
        builder.attributes(attributes);
    }

    if let Some(relations) = conditions.relations {
        builder.relations(relations);
    }

    let options = None;

    let value = connector
        .instance_delete(
            &meta_datasource,
            &datasource,
            class_identifier,
            builder.build()?,
            options,
        )
        .await?;
    result.insert(String::from("result"), value);
    Ok(result)
}

async fn _connector(
    ds_option: &Option<String>,
    context: Arc<Mutex<WorkflowContext>>,
) -> anyhow::Result<(SimpleDataSourceConnector, Option<DataSource>, DataSource)> {
    let sdsc = SimpleDataSourceConnector::default();
    let meta = system_meta_database()?;
    let datasource = datasource_by_id(ds_option, context.clone()).await?;

    Ok((sdsc, Some(meta), datasource))
}
